#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <glib.h>
#include <gnet.h>
#include <argp.h>

#define PORT		59001

static gchar * response = NULL;
static GTcpSocket * serversock = NULL;

static struct {
	gchar * name;
	gchar * version;
	gchar * id;
} args = { 0 };

static struct argp_option arg_opts[] = {
	{ 0, 'n', "name", 0, "the name of the software bundle", 0 },
	{ 0, 'v', "version", 0, "the version of the software bundle", 0 },
	{ 0, 'b', "id", 0, "the bundle id of the software bundle", 0 },
	{ 0 }
};

static error_t parse_args(int key, char * arg, struct argp_state * state);
static struct argp argp = { arg_opts, parse_args, 0, 0 };

static error_t parse_args(int key, char * arg, struct argp_state * state)
{
	switch (key)
	{
		case 'n':	args.name = arg;		break;
		case 'v':	args.version = arg;		break;
		case 'b':	args.id = arg;			break;

		default:
			return ARGP_ERR_UNKNOWN;
	}

	return 0;
}

static gboolean newdata(GIOChannel * gio, GIOCondition condition, gpointer data)
{
	GTcpSocket * sock = data;
	
	gchar buf[25];
	gsize bytes_read;

	g_io_channel_read(gio, buf, sizeof(buf)-1, &bytes_read);
	buf[bytes_read] = '\0';

	if (bytes_read == 0)
	{
		return FALSE;
	}

	if (strcmp(buf, "running") == 0)
	{
		g_io_channel_write_chars(gio, response, strlen(response)+1, NULL, NULL);
		g_io_channel_flush(gio, NULL);
	}
	else if (strcmp(buf, "bundle") == 0)
	{
		GString * str = g_string_new("");
		g_string_printf(str, "%s-%s.stuff", args.name, args.version);
		
		gint fd = open(str->str, O_RDONLY);
		if (fd != -1)
		{
			gchar buf[1024];
			gssize bytes_read;

			do
			{
				bytes_read = read(fd, buf, sizeof(buf));

				if (bytes_read > 0)
				{
					g_io_channel_write_chars(gio, buf, bytes_read, NULL, NULL);
				}
			}
			while (bytes_read > 0);
		}
		
		g_io_channel_flush(gio, NULL);
		gnet_tcp_socket_delete(sock);
		g_string_free(str, TRUE);
		return FALSE;
	}
	else
	{
		gnet_tcp_socket_delete(sock);
		return FALSE;
	}

	return TRUE;
}

static gboolean newclient(GIOChannel * gio, GIOCondition condition, gpointer data)
{
	GTcpSocket * sock = gnet_tcp_socket_server_accept_nonblock(serversock);
	if (sock == NULL)
	{
		//sock error
		fprintf(stderr, "* Error: Could not read new network socket\n");
		return FALSE;
	}

	GIOChannel * sockchan = gnet_tcp_socket_get_io_channel(sock);
	g_io_add_watch(sockchan, G_IO_IN, newdata, sock);

	return TRUE;
}

int main(int argc, char ** argv)
{
	argp_parse(&argp, argc, argv, 0, 0, 0);
	if (args.name == NULL || args.version == NULL || args.id == NULL)
	{
		fprintf(stderr, "* Error: arguments -nvb must be set!\n");
		return 1;
	}

	GString * str = g_string_new("");
	g_string_printf(str, "name=%s\nversion=%s\nid=%s\n", args.name, args.version, args.id);
	response = str->str;
	g_string_free(str, FALSE);
	
	GMainLoop * mainloop = g_main_loop_new(NULL, FALSE);

	serversock = gnet_tcp_socket_server_new_with_port(PORT);
	if (serversock == NULL)
	{
		fprintf(stderr, "* Error: could not create server on port %d\n", PORT);
		return 1;
	}

	GIOChannel * gio = gnet_tcp_socket_get_io_channel(serversock);
	g_io_add_watch(gio, G_IO_IN, newclient, NULL);

	g_main_loop_run(mainloop);

	fprintf(stdout, "Quit.\n");
	return 0;
}

